// Copyright (C) 2012 Volkov Zachar
// This file is part of the "zAxis Game Engine".
// For conditions of distribution and use, see copyright notice zx_Common.hpp

#include "zx_FileSystem.hpp"

namespace zx {

//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
FileStream::FileStream( const String _path, uint _flags ) : DataStream(), m_flags( _flags ), m_handle( 0 )
{
     m_name = FileSystem::FullPath( _path );
     m_access = m_flags & AM_READ_WRITE;
     m_global_mutex = new GlobalMutex( m_name );
     String _path_to = FileSystem::PathTo( m_name );
     bool _dir_exists = FileSystem::IsPhysicalObjectExists( _path_to );

     bool _read = ( _flags & FSF_READ ) != 0;
     bool _write = ( _flags & FSF_WRITE ) != 0;
     bool _create = ( _flags & FSF_CREATE ) != 0;
     bool _trunc = ( _flags & FSF_TRUNCATE ) != 0;
     bool _append = ( _flags & FSF_APPEND ) != 0;

     if( _read && !_write )
     {
          m_handle = fopen( m_name.c_str(), "rb" );
     }
     else if( _write )
     {
          bool _exists = FileSystem::IsPhysicalObjectExists( m_name );
                    
          if( _create )
          {    
               // create directory if no existst
               if( !_dir_exists )
               {
                    FileSystem::CreatePhysicalDirectory( _path_to ); 
               }

               if( FileSystem::IsPhysicalDirectory( _path_to ) )
               {
                    if( _trunc )
                    {
                         m_handle = fopen( m_name.c_str(), "w+b" );
                    }
                    else if( _append )
                    {
                         m_handle = fopen( m_name.c_str(), "a+b" );
                    }
                    else
                    {
                         m_handle = fopen( m_name.c_str(), zxChoose( _exists, "r+b", "w+b" ) );
                    }
               }
          }
          else if( _exists )
          {
               if( _trunc )
               {
                    m_handle = fopen( m_name.c_str(), "w+b" );
               }
               else if( _append )
               {
                    m_handle = fopen( m_name.c_str(), "a+b" );
               }
               else
               {
                    m_handle = fopen( m_name.c_str(), "r+b" );
               }
          }
     }

     if( m_handle )
     {
          fseek( m_handle, 0, SEEK_END );
          m_size = ftell( m_handle );
          fseek( m_handle, 0, SEEK_SET );
     }
}
//----------------------------------------------------------------------------//
FileStream::~FileStream( void )
{
     if( m_handle )
     {
          if( m_flags & FSF_WRITE )
          {
               fflush( m_handle );
          }
          
          fclose( m_handle );
          m_handle = 0;

          if( ( m_flags & FSF_TEMPORARY ) && ( m_flags & FSF_WRITE ) )
          {
               FileSystem::RemovePhysicalFile( m_name );
          }
     }
}
//----------------------------------------------------------------------------//
bool FileStream::GlobalLock( int _time_ms )
{
     return m_global_mutex -> Lock( _time_ms );
}
//----------------------------------------------------------------------------//
void FileStream::GlobalUnlock( void )
{
     m_global_mutex -> Unlock();
}
//----------------------------------------------------------------------------//
bool FileStream::IsOpened( void )
{
     return m_handle != 0;
}
//----------------------------------------------------------------------------//
void FileStream::Flush( void )
{
     if( m_handle && m_flags & FSF_WRITE )
     {    
          fflush( m_handle );
     }
}
//----------------------------------------------------------------------------//
uint FileStream::GetPos( void )
{
     if( m_handle )
     {
          return ftell( m_handle );
     }

     return 0;
}
//----------------------------------------------------------------------------//
void FileStream::SetPos( int _pos, bool _relative )
{
     if( m_handle )
     {
          fseek( m_handle, _pos, zxChoose( _relative, SEEK_CUR, SEEK_SET ) );
     }
}
//----------------------------------------------------------------------------//
uint FileStream::Read( void* _dst, uint _size )
{
     if( m_handle && ( m_flags & FSF_READ ) && _dst )
     {
          if( _size < static_cast< uint >( m_handle -> _cnt ) )
          {
               memcpy( _dst, m_handle -> _ptr, _size );
               m_handle -> _ptr += _size;
               m_handle -> _cnt -= _size;
               return _size;
          }

          return fread( _dst, 1, _size, m_handle );
     }

     return 0;
}
//----------------------------------------------------------------------------//
uint FileStream::Write( const void* _src, uint _size )
{
     if( m_handle && ( m_flags & FSF_WRITE ) && _src )
     {
          return fwrite( _src, 1, _size, m_handle );
     }

     return 0;
}
//----------------------------------------------------------------------------//
bool FileStream::AtEnd( void )
{
     if( m_handle )
     {
          return feof( m_handle ) != 0;
     }

     return false;
}
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//

} // namespace zx
